package com.keguoyu.easymvp.compiler;

import java.lang.ref.Reference;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;

import com.google.auto.service.AutoService;
import com.keguoyu.easymvp.annotation.Nullable;
import com.keguoyu.easymvp.annotation.Receiver;
import com.keguoyu.easymvp.compiler.base.BaseProcessor;
import com.keguoyu.easymvp.compiler.utils.ElementUtils;
import com.keguoyu.easymvp.compiler.receive.ReceiverBuilder;
import com.squareup.javapoet.TypeName;

@AutoService(Processor.class)
@SupportedOptions({ReceiverProcessor.GENERATE_PKG_NAME})
public class ReceiverProcessor extends BaseProcessor {
  static final String GENERATE_PKG_NAME = "generatePkg";

  private boolean mHasProcessed;

  @Override
  public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
    if (mHasProcessed) {
      return false;
    }
    parseInject(roundEnvironment);
    mHasProcessed = true;
    return false;
  }

  private void parseInject(RoundEnvironment roundEnvironment) {
    ElementUtils entries = ElementUtils.fromRoundEnv(roundEnvironment, Receiver.class);
    for (Map.Entry<TypeElement, List<Element>> entry : entries) {
      List<Element> fields = entry.getValue();
      if (fields == null) {
        continue;
      }
      generateClass(entry.getKey(), fields);
    }
  }

  private void generateClass(Element root, List<Element> fields) {
    if (root == null || root.getKind() != ElementKind.CLASS) {
      return;
    }
    String pkg = mElementUtils.getPackageOf(root).toString();
    ReceiverBuilder receiverBuilder =
        new ReceiverBuilder(pkg, root.getSimpleName().toString(), mAutoGenerated, TypeName.get(root.asType()));
    for (Element field : fields) {
      if (field.getKind() != ElementKind.FIELD || field.getModifiers().contains(Modifier.STATIC)) {
        continue;
      }
      Receiver receiver = field.getAnnotation(Receiver.class);
      if (receiver == null) {
        continue;
      }
      String key = receiver.value();
      String fieldName = field.getSimpleName().toString();
      boolean canBeNull = field.getAnnotation(Nullable.class) != null;
      TypeMirror fieldType = mTypeUtils.erasure(field.asType());
      if ("".equals(key)) {
        receiverBuilder.onFieldByType(key, fieldName,
            TypeName.get(fieldType), canBeNull, mTypeUtils.isAssignable(fieldType,
                mTypeUtils.erasure(mElementUtils.getTypeElement(Reference.class.getName()).asType())));
      } else {
        receiverBuilder.onFieldByName(key, fieldName,
            TypeName.get(fieldType), canBeNull, mTypeUtils.isAssignable(fieldType,
                mTypeUtils.erasure(mElementUtils.getTypeElement(Reference.class.getName()).asType())));
      }
      receiverBuilder.onReset(key, fieldName, TypeName.get(fieldType));
    }
    writeClass(receiverBuilder.getPkg(), receiverBuilder.getCls(), receiverBuilder.build());
  }

  @Override
  public Set<String> getSupportedAnnotationTypes() {
    Set<String> set = new HashSet<>();
    set.add(Receiver.class.getName());
    return set;
  }

  @Override
  public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.RELEASE_8;
  }
}
